Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 18cbb5dfe2d6b91ff1313a402ed5afbd625aac6b


Parents : f4d148c
Author : Mark Qvist <mark@unsigned.io>
Date : 2024-08-30T21:17:22+02:00

Added PTT mode. Improved notification display.

Changes

6 files changed, 392 insertions(+), 150 deletions(-)

M sbapp/main.py +199 -145

Diff

diff --git a/sbapp/main.py b/sbapp/main.py
index 148179e3..3b21980c 100644
--- a/sbapp/main.py
+++ b/sbapp/main.py
@@ -1742,162 +1742,214 @@ class SidebandApp(MDApp):
RNS.log("Error while playing message audio:"+str(e))
RNS.trace_exception(e)
-
- def message_record_audio_action(self):
- ss = int(dp(18))
+ def message_ptt_down_action(self, sender=None):
+ self.audio_msg_mode = LXMF.AM_CODEC2_2400
+ self.message_attach_action(attach_type="audio", nodialog=True)
if self.rec_dialog == None:
- if RNS.vendor.platformutils.is_android():
- from plyer import audio
- self.request_microphone_permission()
+ self.message_init_rec_dialog()
+ self.rec_dialog.recording = True
+ el_button = self.messages_view.ids.message_ptt_button
+ el_icon = self.messages_view.ids.message_ptt_button.children[0].children[1]
+ el_button.theme_text_color="Custom"
+ el_button.text_color=mdc("Orange","400")
+ el_button.line_color=mdc("Orange","400")
+ el_icon.theme_text_color="Custom"
+ el_icon.text_color=mdc("Orange","400")
+ def cb(dt):
+ self.msg_audio.start()
+ Clock.schedule_once(cb, 0.15)
+
+
+ def message_ptt_up_action(self, sender=None):
+ self.rec_dialog.recording = False
+ el_button = self.messages_view.ids.message_ptt_button
+ el_icon = self.messages_view.ids.message_ptt_button.children[0].children[1]
+ el_button.theme_text_color="Custom"
+ el_button.text_color=mdc("BlueGray","500")
+ el_button.line_color=mdc("BlueGray","500")
+ el_icon.theme_text_color="Custom"
+ el_icon.text_color=mdc("BlueGray","500")
+ def cb_s(dt):
+ self.msg_audio.stop()
+ self.message_process_audio()
+ self.message_send_action()
+ Clock.schedule_once(cb_s, 0.25)
+
+ def message_process_audio(self):
+ if self.audio_msg_mode == LXMF.AM_OPUS_OGG:
+ from sideband.audioproc import voice_processing
+ proc_path = voice_processing(self.msg_audio._file_path)
+ if proc_path:
+ self.attach_path = proc_path
+ os.unlink(self.msg_audio._file_path)
+ RNS.log("Using voice-processed OPUS data in OGG container", RNS.LOG_DEBUG)
else:
- from sbapp.plyer import audio
-
- self.msg_audio = audio
- self.msg_audio._file_path = self.sideband.rec_cache+"/recording.ogg"
-
- def a_rec_action(sender):
- if not self.rec_dialog.recording:
- RNS.log("Starting recording...") # TODO: Remove
- self.rec_dialog.recording = True
- el = self.rec_dialog.rec_item.children[0].children[0]
- el.ttc = el.theme_text_color; el.tc = el.text_color
- el.theme_text_color="Custom"
- el.text_color=mdc("Red","400")
- el.icon = "stop-circle"
- self.rec_dialog.rec_item.text = "[size="+str(ss)+"]Stop Recording[/size]"
- def cb(dt):
- self.msg_audio.start()
- Clock.schedule_once(cb, 0.15)
+ self.attach_path = self.msg_audio._file_path
+ RNS.log("Using unmodified OPUS data in OGG container", RNS.LOG_DEBUG)
+ else:
+ ap_start = time.time()
+ from sideband.audioproc import voice_processing
+ proc_path = voice_processing(self.msg_audio._file_path)
+ if proc_path:
+ opus_file = pyogg.OpusFile(proc_path)
+ RNS.log("Using voice-processed audio for codec2 encoding", RNS.LOG_DEBUG)
+ else:
+ opus_file = pyogg.OpusFile(self.msg_audio._file_path)
+ RNS.log("Using unprocessed audio data for codec2 encoding", RNS.LOG_DEBUG)
+
+ RNS.log(f"OPUS LOAD {opus_file.frequency}Hz {opus_file.bytes_per_sample*8}bit {opus_file.channels}ch")
+
+ audio = AudioSegment(
+ bytes(opus_file.as_array()),
+ frame_rate=opus_file.frequency,
+ sample_width=opus_file.bytes_per_sample,
+ channels=opus_file.channels,
+ )
+ audio = audio.split_to_mono()[0]
+ audio = audio.apply_gain(-audio.max_dBFS)
+
+ if self.audio_msg_mode >= LXMF.AM_CODEC2_700C and self.audio_msg_mode <= LXMF.AM_CODEC2_3200:
+ audio = audio.set_frame_rate(8000)
+ audio = audio.set_sample_width(2)
+ samples = audio.get_array_of_samples()
+
+ from sideband.audioproc import encode_codec2, detect_codec2
+ if detect_codec2():
+ encoded = encode_codec2(samples, self.audio_msg_mode)
+
+ ap_duration = time.time() - ap_start
+ RNS.log("Audio processing complete in "+RNS.prettytime(ap_duration), RNS.LOG_DEBUG)
+
+ export_path = self.sideband.rec_cache+"/recording.enc"
+ with open(export_path, "wb") as export_file:
+ export_file.write(encoded)
+ self.attach_path = export_path
+ os.unlink(self.msg_audio._file_path)
else:
- RNS.log("Stopping recording...") # TODO: Remove
- self.rec_dialog.recording = False
- self.rec_dialog.rec_item.text = "[size="+str(ss)+"]Start Recording[/size]"
- el = self.rec_dialog.rec_item.children[0].children[0]
- el.icon = "record"
- el.text_color = self.theme_cls._get_text_color()
- self.rec_dialog.play_item.disabled = False
- self.rec_dialog.save_item.disabled = False
- self.msg_audio.stop()
-
- self.msg_rec_a_rec = a_rec_action
-
- def a_play(sender):
- if self.rec_dialog.recording:
- a_rec_action(sender)
-
- if not self.rec_dialog.playing:
- RNS.log("Playing recording...", RNS.LOG_DEBUG)
- self.rec_dialog.playing = True
- self.rec_dialog.play_item.children[0].children[0].icon = "stop"
- self.rec_dialog.play_item.text = "[size="+str(ss)+"]Stop[/size]"
- self.msg_audio.play()
- else:
- RNS.log("Stopping playback...", RNS.LOG_DEBUG)
- self.rec_dialog.playing = False
- self.rec_dialog.play_item.children[0].children[0].icon = "play"
- self.rec_dialog.play_item.text = "[size="+str(ss)+"]Play[/size]"
- self.msg_audio.stop()
+ self.display_codec2_error()
+ return
- self.msg_rec_a_play = a_play
+ def message_init_rec_dialog(self):
+ ss = int(dp(18))
+ if RNS.vendor.platformutils.is_android():
+ from plyer import audio
+ self.request_microphone_permission()
+ else:
+ from sbapp.plyer import audio
+
+ self.msg_audio = audio
+ self.msg_audio._file_path = self.sideband.rec_cache+"/recording.ogg"
+
+ def a_rec_action(sender):
+ if not self.rec_dialog.recording:
+ RNS.log("Starting recording...") # TODO: Remove
+ self.rec_dialog.recording = True
+ el = self.rec_dialog.rec_item.children[0].children[0]
+ el.ttc = el.theme_text_color; el.tc = el.text_color
+ el.theme_text_color="Custom"
+ el.text_color=mdc("Red","400")
+ el.icon = "stop-circle"
+ self.rec_dialog.rec_item.text = "[size="+str(ss)+"]Stop Recording[/size]"
+ def cb(dt):
+ self.msg_audio.start()
+ Clock.schedule_once(cb, 0.10)
- def a_finished(sender):
- RNS.log("Playback finished", RNS.LOG_DEBUG)
+ else:
+ RNS.log("Stopping recording...") # TODO: Remove
+ self.rec_dialog.recording = False
+ self.rec_dialog.rec_item.text = "[size="+str(ss)+"]Start Recording[/size]"
+ el = self.rec_dialog.rec_item.children[0].children[0]
+ el.icon = "record"
+ el.text_color = self.theme_cls._get_text_color()
+ self.rec_dialog.play_item.disabled = False
+ self.rec_dialog.save_item.disabled = False
+ self.msg_audio.stop()
+
+ self.msg_rec_a_rec = a_rec_action
+
+ def a_play(sender):
+ if self.rec_dialog.recording:
+ a_rec_action(sender)
+
+ if not self.rec_dialog.playing:
+ RNS.log("Playing recording...", RNS.LOG_DEBUG)
+ self.rec_dialog.playing = True
+ self.rec_dialog.play_item.children[0].children[0].icon = "stop"
+ self.rec_dialog.play_item.text = "[size="+str(ss)+"]Stop[/size]"
+ self.msg_audio.play()
+ else:
+ RNS.log("Stopping playback...", RNS.LOG_DEBUG)
self.rec_dialog.playing = False
self.rec_dialog.play_item.children[0].children[0].icon = "play"
self.rec_dialog.play_item.text = "[size="+str(ss)+"]Play[/size]"
-
- self.msg_audio._finished_callback = a_finished
-
- def a_save(sender):
- if self.rec_dialog.recording:
- a_rec_action(sender)
- self.rec_dialog_is_open = False
- self.rec_dialog.dismiss()
+ self.msg_audio.stop()
- try:
- if self.audio_msg_mode == LXMF.AM_OPUS_OGG:
- from sideband.audioproc import voice_processing
- proc_path = voice_processing(self.msg_audio._file_path)
- if proc_path:
- self.attach_path = proc_path
- os.unlink(self.msg_audio._file_path)
- RNS.log("Using voice-processed OPUS data in OGG container", RNS.LOG_DEBUG)
- else:
- self.attach_path = self.msg_audio._file_path
- RNS.log("Using unmodified OPUS data in OGG container", RNS.LOG_DEBUG)
+ self.msg_rec_a_play = a_play
+
+ def a_finished(sender):
+ RNS.log("Playback finished", RNS.LOG_DEBUG)
+ self.rec_dialog.playing = False
+ self.rec_dialog.play_item.children[0].children[0].icon = "play"
+ self.rec_dialog.play_item.text = "[size="+str(ss)+"]Play[/size]"
+
+ self.msg_audio._finished_callback = a_finished
+
+ def a_save(sender):
+ if self.rec_dialog.recording:
+ a_rec_action(sender)
+ self.rec_dialog_is_open = False
+ self.rec_dialog.dismiss()
+
+ try:
+ if self.audio_msg_mode == LXMF.AM_OPUS_OGG:
+ from sideband.audioproc import voice_processing
+ proc_path = voice_processing(self.msg_audio._file_path)
+ if proc_path:
+ self.attach_path = proc_path
+ os.unlink(self.msg_audio._file_path)
+ RNS.log("Using voice-processed OPUS data in OGG container", RNS.LOG_DEBUG)
else:
- ap_start = time.time()
- from sideband.audioproc import voice_processing
- proc_path = voice_processing(self.msg_audio._file_path)
+ self.attach_path = self.msg_audio._file_path
+ RNS.log("Using unmodified OPUS data in OGG container", RNS.LOG_DEBUG)
+ else:
+ self.message_process_audio()
- if proc_path:
- opus_file = pyogg.OpusFile(proc_path)
- RNS.log("Using voice-processed audio for codec2 encoding", RNS.LOG_DEBUG)
- else:
- opus_file = pyogg.OpusFile(self.msg_audio._file_path)
- RNS.log("Using unprocessed audio data for codec2 encoding", RNS.LOG_DEBUG)
-
- audio = AudioSegment(
- bytes(opus_file.as_array()),
- frame_rate=opus_file.frequency,
- sample_width=opus_file.bytes_per_sample,
- channels=opus_file.channels,
- )
- audio = audio.split_to_mono()[0]
- audio = audio.apply_gain(-audio.max_dBFS)
-
- if self.audio_msg_mode >= LXMF.AM_CODEC2_700C and self.audio_msg_mode <= LXMF.AM_CODEC2_3200:
- audio = audio.set_frame_rate(8000)
- audio = audio.set_sample_width(2)
- samples = audio.get_array_of_samples()
-
- from sideband.audioproc import encode_codec2, detect_codec2
- if detect_codec2():
- encoded = encode_codec2(samples, self.audio_msg_mode)
-
- ap_duration = time.time() - ap_start
- RNS.log("Audio processing complete in "+RNS.prettytime(ap_duration), RNS.LOG_DEBUG)
-
- export_path = self.sideband.rec_cache+"/recording.enc"
- with open(export_path, "wb") as export_file:
- export_file.write(encoded)
- self.attach_path = export_path
- os.unlink(self.msg_audio._file_path)
- else:
- self.display_codec2_error()
- return
+ self.update_message_widgets()
+ toast("Added recorded audio to message")
+
+ except Exception as e:
+ RNS.trace_exception(e)
- self.update_message_widgets()
- toast("Added recorded audio to message")
-
- except Exception as e:
- RNS.trace_exception(e)
+ self.msg_rec_a_save = a_save
+
+ cancel_button = MDRectangleFlatButton(text="Cancel", font_size=dp(18))
+ rec_item = DialogItem(IconLeftWidget(icon="record", on_release=a_rec_action), text="[size="+str(ss)+"]Start Recording[/size]", on_release=a_rec_action)
+ play_item = DialogItem(IconLeftWidget(icon="play", on_release=a_play), text="[size="+str(ss)+"]Play[/size]", on_release=a_play, disabled=True)
+ save_item = DialogItem(IconLeftWidget(icon="content-save-move-outline", on_release=a_save), text="[size="+str(ss)+"]Save to message[/size]", on_release=a_save, disabled=True)
+ self.rec_dialog = MDDialog(
+ title="Record Audio",
+ type="simple",
+ # text="Test\n",
+ items=[
+ rec_item,
+ play_item,
+ save_item,
+ ],
+ buttons=[ cancel_button ],
+ width_offset=dp(12),
+ )
+ cancel_button.bind(on_release=self.rec_dialog.dismiss)
+ self.rec_dialog.recording = False
+ self.rec_dialog.playing = False
+ self.rec_dialog.rec_item = rec_item
+ self.rec_dialog.play_item = play_item
+ self.rec_dialog.save_item = save_item
- self.msg_rec_a_save = a_save
-
- cancel_button = MDRectangleFlatButton(text="Cancel", font_size=dp(18))
- rec_item = DialogItem(IconLeftWidget(icon="record", on_release=a_rec_action), text="[size="+str(ss)+"]Start Recording[/size]", on_release=a_rec_action)
- play_item = DialogItem(IconLeftWidget(icon="play", on_release=a_play), text="[size="+str(ss)+"]Play[/size]", on_release=a_play, disabled=True)
- save_item = DialogItem(IconLeftWidget(icon="content-save-move-outline", on_release=a_save), text="[size="+str(ss)+"]Save to message[/size]", on_release=a_save, disabled=True)
- self.rec_dialog = MDDialog(
- title="Record Audio",
- type="simple",
- # text="Test\n",
- items=[
- rec_item,
- play_item,
- save_item,
- ],
- buttons=[ cancel_button ],
- width_offset=dp(12),
- )
- cancel_button.bind(on_release=self.rec_dialog.dismiss)
- self.rec_dialog.recording = False
- self.rec_dialog.playing = False
- self.rec_dialog.rec_item = rec_item
- self.rec_dialog.play_item = play_item
- self.rec_dialog.save_item = save_item
+ def message_record_audio_action(self):
+ ss = int(dp(18))
+ if self.rec_dialog == None:
+ self.message_init_rec_dialog()
else:
self.rec_dialog.play_item.disabled = True
@@ -1910,7 +1962,7 @@ class SidebandApp(MDApp):
self.rec_dialog_is_open = True
self.rec_dialog.update_width()
- def message_attach_action(self, attach_type=None):
+ def message_attach_action(self, attach_type=None, nodialog=False):
file_attach_types = ["lbimg", "defimg", "hqimg", "file"]
rec_attach_types = ["audio"]
@@ -1918,10 +1970,12 @@ class SidebandApp(MDApp):
self.rec_dialog_is_open = False
if attach_type in file_attach_types:
self.attach_type = attach_type
- self.message_select_file_action()
+ if not nodialog:
+ self.message_select_file_action()
elif attach_type in rec_attach_types:
self.attach_type = attach_type
- self.message_record_audio_action()
+ if not nodialog:
+ self.message_record_audio_action()
def message_attachment_action(self, sender):
self.rec_dialog_is_open = False

diff --git a/sbapp/plyer/platforms/linux/notification.py b/sbapp/plyer/platforms/linux/notification.py
index 65f5131d..552519ea 100644
--- a/sbapp/plyer/platforms/linux/notification.py
+++ b/sbapp/plyer/platforms/linux/notification.py
@@ -63,7 +63,7 @@ class NotifyDbus(Notification):
def _notify(self, **kwargs):
summary = kwargs.get('title', "title")
body = kwargs.get('message', "body")
- app_name = kwargs.get('app_name', '')
+ app_name = "Sideband"
app_icon = kwargs.get('app_icon', '')
timeout = kwargs.get('timeout', 10)
actions = kwargs.get('actions', [])

diff --git a/sbapp/plyer/platforms/macosx/notification.py b/sbapp/plyer/platforms/macosx/notification.py
index 35ce67ab..6a94bbd0 100644
--- a/sbapp/plyer/platforms/macosx/notification.py
+++ b/sbapp/plyer/platforms/macosx/notification.py
@@ -26,7 +26,7 @@ class OSXNotification(Notification):
def _notify(self, **kwargs):
title = kwargs.get('title', '')
message = kwargs.get('message', '')
- app_name = kwargs.get('app_name', '')
+ app_name = "Sideband"
# app_icon, timeout, ticker are not supported (yet)
notification = NSUserNotification.alloc().init()

diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py
index fcf25126..e958380a 100644
--- a/sbapp/sideband/core.py
+++ b/sbapp/sideband/core.py
@@ -112,6 +112,8 @@ class SidebandCore():
self.is_service = is_service
self.is_client = is_client
self.is_daemon = is_daemon
+ self.msg_audio = None
+ self.last_msg_audio = None
self.db = None
if not self.is_service and not self.is_client:
@@ -938,6 +940,25 @@ class SidebandCore():
RNS.log("Error while checking trust for "+RNS.prettyhexrep(context_dest)+": "+str(e), RNS.LOG_ERROR)
return False
+ def ptt_enabled(self, context_dest, conv_data = None):
+ try:
+ if conv_data == None:
+ existing_conv = self._db_conversation(context_dest)
+ else:
+ existing_conv = conv_data
+
+ if existing_conv != None:
+ data_dict = existing_conv["data"]
+ if data_dict != None:
+ if "ptt_enabled" in data_dict:
+ return data_dict["ptt_enabled"]
+
+ return False
+
+ except Exception as e:
+ RNS.log("Error while checking PTT-enabled for "+RNS.prettyhexrep(context_dest)+": "+str(e), RNS.LOG_ERROR)
+ return False
+
def should_send_telemetry(self, context_dest, conv_data=None):
try:
if self.config["telemetry_enabled"]:
@@ -1092,6 +1113,9 @@ class SidebandCore():
def conversation_set_object(self, context_dest, is_object):
self._db_conversation_set_object(context_dest, is_object)
+ def conversation_set_ptt_enabled(self, context_dest, ptt_enabled):
+ self._db_conversation_set_ptt_enabled(context_dest, ptt_enabled)
+
def send_telemetry_in_conversation(self, context_dest):
self._db_conversation_set_telemetry(context_dest, True)
@@ -2069,6 +2093,32 @@ class SidebandCore():
RNS.log("Retrying operation...", RNS.LOG_ERROR)
self._db_conversation_set_object(context_dest, is_object, is_retry=True)
+ def _db_conversation_set_ptt_enabled(self, context_dest, ptt_enabled=False):
+ conv = self._db_conversation(context_dest)
+ data_dict = conv["data"]
+ if data_dict == None:
+ data_dict = {}
+
+ data_dict["ptt_enabled"] = ptt_enabled
+ packed_dict = msgpack.packb(data_dict)
+
+ db = self.__db_connect()
+ dbc = db.cursor()
+
+ query = "UPDATE conv set data = ? where dest_context = ?"
+ data = (packed_dict, context_dest)
+ dbc.execute(query, data)
+ result = dbc.fetchall()
+
+ try:
+ db.commit()
+ except Exception as e:
+ RNS.log("An error occurred while updating conversation PTT option: "+str(e), RNS.LOG_ERROR)
+ self.__db_reconnect()
+ if not is_retry:
+ RNS.log("Retrying operation...", RNS.LOG_ERROR)
+ self._db_conversation_set_ptt_enabled(context_dest, ptt_enabled, is_retry=True)
+
def _db_conversation_set_trusted(self, context_dest, trusted):
db = self.__db_connect()
dbc = db.cursor()
@@ -3761,6 +3811,7 @@ class SidebandCore():
def lxm_ingest(self, message, originator = False):
should_notify = False
is_trusted = False
+ ptt_enabled = False
telemetry_only = False
own_command = False
unread_reason_tx = False
@@ -3771,6 +3822,7 @@ class SidebandCore():
else:
context_dest = message.source_hash
is_trusted = self.is_trusted(context_dest)
+ ptt_enabled = self.ptt_enabled(context_dest)
if originator and LXMF.FIELD_COMMANDS in message.fields:
own_command = True
@@ -3813,6 +3865,74 @@ class SidebandCore():
if self.gui_display() == "conversations_screen" and self.gui_foreground():
should_notify = False
+ ### PTT #######################################################################
+ if not originator and LXMF.FIELD_AUDIO in message.fields and ptt_enabled:
+ if self.msg_audio == None:
+ if RNS.vendor.platformutils.is_android():
+ from plyer import audio
+ else:
+ from sbapp.plyer import audio
+
+ RNS.log("Audio init done")
+ self.msg_audio = audio
+ try:
+ temp_path = None
+ audio_field = message.fields[LXMF.FIELD_AUDIO]
+ if self.last_msg_audio != audio_field[1]:
+ RNS.log("Reloading audio source", RNS.LOG_DEBUG)
+ if len(audio_field[1]) > 10:
+ self.last_msg_audio = audio_field[1]
+ else:
+ self.last_msg_audio = None
+ return
+
+ if audio_field[0] == LXMF.AM_OPUS_OGG:
+ temp_path = self.rec_cache+"/msg.ogg"
+ with open(temp_path, "wb") as af:
+ af.write(self.last_msg_audio)
+
+ elif audio_field[0] >= LXMF.AM_CODEC2_700C and audio_field[0] <= LXMF.AM_CODEC2_3200:
+ temp_path = self.rec_cache+"/msg.ogg"
+ from sideband.audioproc import samples_to_ogg, decode_codec2, detect_codec2
+
+ target_rate = 8000
+ if RNS.vendor.platformutils.is_linux():
+ target_rate = 48000
+
+ if detect_codec2():
+ if samples_to_ogg(decode_codec2(audio_field[1], audio_field[0]), temp_path, input_rate=8000, output_rate=target_rate):
+ RNS.log("Wrote OGG file to: "+temp_path, RNS.LOG_DEBUG)
+ else:
+ RNS.log("OGG write failed", RNS.LOG_DEBUG)
+ else:
+ self.last_msg_audio = None
+ return
+
+ else:
+ # Unimplemented audio type
+ pass
+
+ self.msg_sound = self.msg_audio
+ self.msg_sound._file_path = temp_path
+ self.msg_sound.reload()
+
+ if self.msg_sound != None and self.msg_sound.playing():
+ RNS.log("Stopping playback", RNS.LOG_DEBUG)
+ self.msg_sound.stop()
+ else:
+ if self.msg_sound != None:
+ RNS.log("Starting playback", RNS.LOG_DEBUG)
+ self.msg_sound.play()
+ should_notify = False
+ else:
+ RNS.log("Playback was requested, but no audio data was loaded for playback", RNS.LOG_ERROR)
+
+ except Exception as e:
+ RNS.log("Error while playing message audio:"+str(e))
+ RNS.trace_exception(e)
+ ###############################################################################
+
+
if self.is_client:
should_notify = False
@@ -3824,7 +3944,14 @@ class SidebandCore():
text = message.content.decode("utf-8")
notification_content = text[:nlen]
if len(text) > nlen:
- text += "..."
+ notification_content += " [...]"
+
+ if len(text) < 2 and LXMF.FIELD_AUDIO in message.fields:
+ notification_content = "Audio message"
+ if len(text) < 2 and LXMF.FIELD_IMAGE in message.fields:
+ notification_content = "Image"
+ if len(text) < 2 and LXMF.FIELD_FILE_ATTACHMENTS in message.fields:
+ notification_content = "File attachment"
self.notify(title=self.peer_display_name(context_dest), content=notification_content, group="LXM", context_id=RNS.hexrep(context_dest, delimit=False))

diff --git a/sbapp/ui/conversations.py b/sbapp/ui/conversations.py
index c48d5bff..4427af3f 100644
--- a/sbapp/ui/conversations.py
+++ b/sbapp/ui/conversations.py
@@ -36,6 +36,7 @@ class ConvSettings(BoxLayout):
telemetry = BooleanProperty()
allow_requests = BooleanProperty()
is_object = BooleanProperty()
+ ptt_enabled = BooleanProperty()
class Conversations():
def __init__(self, app):
@@ -192,6 +193,7 @@ class Conversations():
if not context_dest in self.added_item_dests:
existing_conv = self.app.sideband._db_conversation(context_dest)
is_object = self.app.sideband.is_object(context_dest, conv_data=existing_conv)
+ ptt_enabled = self.app.sideband.ptt_enabled(context_dest, conv_data=existing_conv)
iconl = self.get_icon(conv)
item = OneLineAvatarIconListItem(text=peer_disp_name, on_release=self.app.conversation_action)
item.add_widget(iconl)
@@ -210,13 +212,14 @@ class Conversations():
disp_name = self.app.sideband.raw_display_name(dest)
is_trusted = self.app.sideband.is_trusted(dest, conv_data=cd)
is_object = self.app.sideband.is_object(dest, conv_data=cd)
+ ptt_enabled = self.app.sideband.ptt_enabled(dest, conv_data=cd)
send_telemetry = self.app.sideband.should_send_telemetry(dest, conv_data=cd)
allow_requests = self.app.sideband.requests_allowed_from(dest, conv_data=cd)
yes_button = MDRectangleFlatButton(text="Save",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_accept, text_color=self.app.color_accept)
no_button = MDRectangleFlatButton(text="Cancel",font_size=dp(18))
dialog_content = ConvSettings(disp_name=disp_name, context_dest=RNS.hexrep(dest, delimit=False), trusted=is_trusted,
- telemetry=send_telemetry, allow_requests=allow_requests, is_object=is_object)
+ telemetry=send_telemetry, allow_requests=allow_requests, is_object=is_object, ptt_enabled=ptt_enabled)
dialog_content.ids.name_field.font_name = self.app.input_font
dialog = MDDialog(
@@ -235,6 +238,7 @@ class Conversations():
telemetry = dialog.d_content.ids["telemetry_switch"].active
allow_requests = dialog.d_content.ids["allow_requests_switch"].active
conv_is_object = dialog.d_content.ids["is_object_switch"].active
+ ptt_is_enabled = dialog.d_content.ids["ptt_enabled_switch"].active
if trusted:
self.app.sideband.trusted_conversation(dest)
else:
@@ -255,6 +259,13 @@ class Conversations():
else:
self.app.sideband.conversation_set_object(dest, False)
+ if ptt_is_enabled:
+ RNS.log("Setting PTT enabled")
+ self.app.sideband.conversation_set_ptt_enabled(dest, True)
+ else:
+ RNS.log("Setting PTT disabled")
+ self.app.sideband.conversation_set_ptt_enabled(dest, False)
+
self.app.sideband.named_conversation(name, dest)
except Exception as e:
@@ -604,6 +615,21 @@ Builder.load_string("""
pos_hint: {"center_y": 0.43}
active: root.allow_requests
+ MDBoxLayout:
+ orientation: "horizontal"
+ size_hint_y: None
+ padding: [0,0,dp(8),0]
+ height: dp(32)
+ MDLabel:
+ id: ptt_enabled_label
+ text: "PTT Enabled"
+ font_style: "H6"
+
+ MDSwitch:
+ id: ptt_enabled_switch
+ pos_hint: {"center_y": 0.43}
+ active: root.ptt_enabled
+
MDBoxLayout:
orientation: "horizontal"
size_hint_y: None

diff --git a/sbapp/ui/messages.py b/sbapp/ui/messages.py
index 6dcbd672..1d8d8b39 100644
--- a/sbapp/ui/messages.py
+++ b/sbapp/ui/messages.py
@@ -57,6 +57,7 @@ class Messages():
self.context_dest = context_dest
self.source_dest = context_dest
self.is_trusted = self.app.sideband.is_trusted(self.context_dest)
+ self.ptt_enabled = self.app.sideband.ptt_enabled(self.context_dest)
self.screen = self.app.root.ids.screen_manager.get_screen("messages_screen")
self.ids = self.screen.ids
@@ -216,6 +217,15 @@ class Messages():
w.dmenu.items.append(w.dmenu.retry_item)
+ def hide_widget(self, wid, dohide=True):
+ if hasattr(wid, 'saved_attrs'):
+ if not dohide:
+ wid.height, wid.size_hint_y, wid.opacity, wid.disabled = wid.saved_attrs
+ del wid.saved_attrs
+ elif dohide:
+ wid.saved_attrs = wid.height, wid.size_hint_y, wid.opacity, wid.disabled
+ wid.height, wid.size_hint_y, wid.opacity, wid.disabled = 0, None, 0, True
+
def update_widget(self):
if self.app.sideband.config["dark_ui"]:
intensity_msgs = intensity_msgs_dark
@@ -228,6 +238,11 @@ class Messages():
self.ids.message_text.font_name = self.app.input_font
+ if self.ptt_enabled:
+ self.hide_widget(self.ids.message_ptt, False)
+ else:
+ self.hide_widget(self.ids.message_ptt, True)
+
if self.loading_earlier_messages:
self.new_messages.reverse()
@@ -1020,7 +1035,27 @@ MDScreen:
icon: "key-wireless"
text: "Query Network For Keys"
on_release: root.app.key_query_action(self)
-
+
+ BoxLayout:
+ id: message_ptt
+ padding: [dp(16), dp(8), dp(16), dp(8)]
+ spacing: dp(24)
+ size_hint_y: None
+ height: self.minimum_height
+
+ MDRectangleFlatIconButton:
+ id: message_ptt_button
+ icon: "microphone"
+ text: "PTT"
+ size_hint_x: 1.0
+ padding: [dp(10), dp(13), dp(10), dp(14)]
+ icon_size: dp(24)
+ font_size: dp(16)
+ on_press: root.app.message_ptt_down_action(self)
+ on_release: root.app.message_ptt_up_action(self)
+ _no_ripple_effect: True
+ background_normal: ""
+ background_down: ""
BoxLayout:
id: message_input_part


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────